#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <common_header.h>
#include <cfloat>

namespace primal
{
	namespace d3dx
	{
		struct swapchain_desc : DXGI_SWAP_CHAIN_DESC1
		{
			swapchain_desc() = default;
			constexpr void init_unvsync(u32 width, u32 height, u32 back_buffer_count)
			{
				this->Width = width;
				this->Height = height;
				this->Format = DXGI_FORMAT_R8G8B8A8_UNORM;
				this->Stereo = FALSE;
				this->SampleDesc.Count = 1u;
				this->SampleDesc.Quality = 0u;
				this->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
				this->BufferCount = back_buffer_count;
				this->Scaling = DXGI_SCALING_STRETCH;
				this->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
				this->AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
				this->Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
			}
		};

		constexpr struct
		{
			const D3D12_COMMAND_QUEUE_DESC direct{
				D3D12_COMMAND_LIST_TYPE_DIRECT,        //D3D12_COMMAND_LIST_TYPE      Type;
				D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,   //INT 						  Priority;
				D3D12_COMMAND_QUEUE_FLAG_NONE,		   //D3D12_COMMAND_QUEUE_FLAGS    Flags;
				0u,									   //UINT 						  NodeMask;
			};

			const D3D12_COMMAND_QUEUE_DESC copy{
				D3D12_COMMAND_LIST_TYPE_COPY,          //D3D12_COMMAND_LIST_TYPE      Type;
				D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,   //INT 						  Priority;
				D3D12_COMMAND_QUEUE_FLAG_NONE,		   //D3D12_COMMAND_QUEUE_FLAGS    Flags;
				0u,									   //UINT 						  NodeMask;
			};
		}queue_descriptors;

		constexpr struct
		{
			const DXGI_SWAP_CHAIN_FULLSCREEN_DESC full{
				{},                                       //DXGI_RATIONAL                RefreshRate;
				DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,  	  //DXGI_MODE_SCANLINE_ORDER 	 ScanlineOrdering;
				DXGI_MODE_SCALING_UNSPECIFIED,			  //DXGI_MODE_SCALING 			 Scaling;
				FALSE									  //BOOL 						 Windowed;
			};

			const DXGI_SWAP_CHAIN_FULLSCREEN_DESC screen{
				{},                                       //DXGI_RATIONAL                RefreshRate;
				DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,  	  //DXGI_MODE_SCANLINE_ORDER 	 ScanlineOrdering;
				DXGI_MODE_SCALING_UNSPECIFIED,			  //DXGI_MODE_SCALING 			 Scaling;
				TRUE									  //BOOL 						 Windowed;
			};
		}swap_chain_fullscreen_desc{};

		constexpr struct
		{
			const D3D12_HEAP_PROPERTIES default_heap
			{
				D3D12_HEAP_TYPE_DEFAULT,
				D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
				D3D12_MEMORY_POOL_UNKNOWN,
				0,
				0
			};
			const D3D12_HEAP_PROPERTIES upload_heap
			{
				D3D12_HEAP_TYPE_UPLOAD,
				D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
				D3D12_MEMORY_POOL_UNKNOWN,
				0,
				0
			};
		}heap_properties{};

		constexpr struct
		{
			const D3D12_BLEND_DESC Opaque =
			{
				FALSE, // AlphaToCoverageEnable
				FALSE, // IndependentBlendEnable
				{ {
					FALSE, // BlendEnable
					FALSE, // LogicOpEnable
					D3D12_BLEND_ONE, // SrcBlend
					D3D12_BLEND_ZERO, // DestBlend
					D3D12_BLEND_OP_ADD, // BlendOp
					D3D12_BLEND_ONE, // SrcBlendAlpha
					D3D12_BLEND_ZERO, // DestBlendAlpha
					D3D12_BLEND_OP_ADD, // BlendOpAlpha
					D3D12_LOGIC_OP_NOOP,
					D3D12_COLOR_WRITE_ENABLE_ALL
				} }
			};

			const D3D12_BLEND_DESC AlphaBlend =
			{
				FALSE, // AlphaToCoverageEnable
				FALSE, // IndependentBlendEnable
				{ {
					TRUE, // BlendEnable
					FALSE, // LogicOpEnable
					D3D12_BLEND_ONE, // SrcBlend
					D3D12_BLEND_INV_SRC_ALPHA, // DestBlend
					D3D12_BLEND_OP_ADD, // BlendOp
					D3D12_BLEND_ONE, // SrcBlendAlpha
					D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha
					D3D12_BLEND_OP_ADD, // BlendOpAlpha
					D3D12_LOGIC_OP_NOOP,
					D3D12_COLOR_WRITE_ENABLE_ALL
				} }
			};

			const D3D12_BLEND_DESC Additive =
			{
				FALSE, // AlphaToCoverageEnable
				FALSE, // IndependentBlendEnable
				{ {
					TRUE, // BlendEnable
					FALSE, // LogicOpEnable
					D3D12_BLEND_SRC_ALPHA, // SrcBlend
					D3D12_BLEND_ONE, // DestBlend
					D3D12_BLEND_OP_ADD, // BlendOp
					D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha
					D3D12_BLEND_ONE, // DestBlendAlpha
					D3D12_BLEND_OP_ADD, // BlendOpAlpha
					D3D12_LOGIC_OP_NOOP,
					D3D12_COLOR_WRITE_ENABLE_ALL
				} }
			};

			const D3D12_BLEND_DESC NonPremultiplied =
			{
				FALSE, // AlphaToCoverageEnable
				FALSE, // IndependentBlendEnable
				{ {
					TRUE, // BlendEnable
					FALSE, // LogicOpEnable
					D3D12_BLEND_SRC_ALPHA, // SrcBlend
					D3D12_BLEND_INV_SRC_ALPHA, // DestBlend
					D3D12_BLEND_OP_ADD, // BlendOp
					D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha
					D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha
					D3D12_BLEND_OP_ADD, // BlendOpAlpha
					D3D12_LOGIC_OP_NOOP,
					D3D12_COLOR_WRITE_ENABLE_ALL
				} }
			};
		}blend_descs{};

		constexpr struct
		{
			const D3D12_DEPTH_STENCIL_DESC DepthNone =
			{
				FALSE, // DepthEnable
				D3D12_DEPTH_WRITE_MASK_ZERO,
				D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc
				FALSE, // StencilEnable
				D3D12_DEFAULT_STENCIL_READ_MASK,
				D3D12_DEFAULT_STENCIL_WRITE_MASK,
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				}, // FrontFace
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				} // BackFace
			};

			const D3D12_DEPTH_STENCIL_DESC DepthDefault =
			{
				TRUE, // DepthEnable
				D3D12_DEPTH_WRITE_MASK_ALL,
				D3D12_COMPARISON_FUNC_LESS, // DepthFunc
				FALSE, // StencilEnable
				D3D12_DEFAULT_STENCIL_READ_MASK,
				D3D12_DEFAULT_STENCIL_WRITE_MASK,
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				}, // FrontFace
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				} // BackFace
			};

			const D3D12_DEPTH_STENCIL_DESC DepthRead =
			{
				TRUE, // DepthEnable
				D3D12_DEPTH_WRITE_MASK_ZERO,
				D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc
				FALSE, // StencilEnable
				D3D12_DEFAULT_STENCIL_READ_MASK,
				D3D12_DEFAULT_STENCIL_WRITE_MASK,
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				}, // FrontFace
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				} // BackFace
			};

			const D3D12_DEPTH_STENCIL_DESC DepthReverseZ =
			{
				TRUE, // DepthEnable
				D3D12_DEPTH_WRITE_MASK_ALL,
				D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc
				FALSE, // StencilEnable
				D3D12_DEFAULT_STENCIL_READ_MASK,
				D3D12_DEFAULT_STENCIL_WRITE_MASK,
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				}, // FrontFace
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				} // BackFace
			};

			const D3D12_DEPTH_STENCIL_DESC DepthReadReverseZ =
			{
				TRUE, // DepthEnable
				D3D12_DEPTH_WRITE_MASK_ZERO,
				D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc
				FALSE, // StencilEnable
				D3D12_DEFAULT_STENCIL_READ_MASK,
				D3D12_DEFAULT_STENCIL_WRITE_MASK,
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				}, // FrontFace
				{
					D3D12_STENCIL_OP_KEEP, // StencilFailOp
					D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
					D3D12_STENCIL_OP_KEEP, // StencilPassOp
					D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
				} // BackFace
			};
		}depth_stencil_descs{};

		constexpr struct
		{
			const D3D12_RASTERIZER_DESC CullNone =
			{
				D3D12_FILL_MODE_SOLID,
				D3D12_CULL_MODE_NONE,
				FALSE, // FrontCounterClockwise
				D3D12_DEFAULT_DEPTH_BIAS,
				D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
				D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
				TRUE, // DepthClipEnable
				TRUE, // MultisampleEnable
				FALSE, // AntialiasedLineEnable
				0, // ForcedSampleCount
				D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
			};

			const D3D12_RASTERIZER_DESC CullClockwise =
			{
				D3D12_FILL_MODE_SOLID,
				D3D12_CULL_MODE_FRONT,
				FALSE, // FrontCounterClockwise
				D3D12_DEFAULT_DEPTH_BIAS,
				D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
				D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
				TRUE, // DepthClipEnable
				TRUE, // MultisampleEnable
				FALSE, // AntialiasedLineEnable
				0, // ForcedSampleCount
				D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
			};

			const D3D12_RASTERIZER_DESC CullCounterClockwise =
			{
				D3D12_FILL_MODE_SOLID,
				D3D12_CULL_MODE_BACK,
				FALSE, // FrontCounterClockwise
				D3D12_DEFAULT_DEPTH_BIAS,
				D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
				D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
				TRUE, // DepthClipEnable
				TRUE, // MultisampleEnable
				FALSE, // AntialiasedLineEnable
				0, // ForcedSampleCount
				D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
			};

			const D3D12_RASTERIZER_DESC Wireframe =
			{
				D3D12_FILL_MODE_WIREFRAME,
				D3D12_CULL_MODE_NONE,
				FALSE, // FrontCounterClockwise
				D3D12_DEFAULT_DEPTH_BIAS,
				D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
				D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
				TRUE, // DepthClipEnable
				TRUE, // MultisampleEnable
				FALSE, // AntialiasedLineEnable
				0, // ForcedSampleCount
				D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
			};
		}rasterizer_descs{};

		constexpr struct
		{
			const DXGI_FORMAT render_target{ DXGI_FORMAT_R8G8B8A8_UNORM };
			const DXGI_FORMAT depth_stencil{ DXGI_FORMAT_D24_UNORM_S8_UINT };
			const DXGI_SAMPLE_DESC sampler{ 1u,0u };
		}default_formats{};


		namespace sampler
		{
			constexpr D3D12_STATIC_SAMPLER_DESC StaticPointWrap
			(
				unsigned int shaderRegister,
				D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
				unsigned int registerSpace = 0u
			) noexcept
			{
				const D3D12_STATIC_SAMPLER_DESC s_desc = {
					D3D12_FILTER_MIN_MAG_MIP_POINT,
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
					0, // MipLODBias
					D3D12_MAX_MAXANISOTROPY,
					D3D12_COMPARISON_FUNC_NEVER,
					D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
					0, // MinLOD
					FLT_MAX, // MaxLOD
					0, // ShaderRegister
					0, // RegisterSpace
					D3D12_SHADER_VISIBILITY_ALL,
				};
				D3D12_STATIC_SAMPLER_DESC desc = s_desc;
				desc.ShaderRegister = shaderRegister;
				desc.ShaderVisibility = shaderVisibility;
				desc.RegisterSpace = registerSpace;
				return desc;
			}

			constexpr D3D12_STATIC_SAMPLER_DESC StaticPointClamp
			(
				unsigned int shaderRegister,
				D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
				unsigned int registerSpace = 0u
			) noexcept
			{
				const D3D12_STATIC_SAMPLER_DESC s_desc = {
					D3D12_FILTER_MIN_MAG_MIP_POINT,
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW
					0, // MipLODBias
					D3D12_MAX_MAXANISOTROPY,
					D3D12_COMPARISON_FUNC_NEVER,
					D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
					0, // MinLOD
					FLT_MAX, // MaxLOD
					0, // ShaderRegister
					0, // RegisterSpace
					D3D12_SHADER_VISIBILITY_ALL,
				};
				D3D12_STATIC_SAMPLER_DESC desc = s_desc;
				desc.ShaderRegister = shaderRegister;
				desc.ShaderVisibility = shaderVisibility;
				desc.RegisterSpace = registerSpace;
				return desc;
			};

			constexpr D3D12_STATIC_SAMPLER_DESC StaticLinearWrap(
				unsigned int shaderRegister,
				D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
				unsigned int registerSpace = 0u) noexcept
			{
				const D3D12_STATIC_SAMPLER_DESC s_desc = {
					D3D12_FILTER_MIN_MAG_MIP_LINEAR,
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
					0, // MipLODBias
					D3D12_MAX_MAXANISOTROPY,
					D3D12_COMPARISON_FUNC_NEVER,
					D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
					0, // MinLOD
					FLT_MAX, // MaxLOD
					0, // ShaderRegister
					0, // RegisterSpace
					D3D12_SHADER_VISIBILITY_ALL,
				};
				D3D12_STATIC_SAMPLER_DESC desc = s_desc;
				desc.ShaderRegister = shaderRegister;
				desc.ShaderVisibility = shaderVisibility;
				desc.RegisterSpace = registerSpace;
				return desc;
			};

			constexpr D3D12_STATIC_SAMPLER_DESC StaticAnisotropicWrap(
				unsigned int shaderRegister,
				D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
				unsigned int registerSpace = 0u) noexcept
			{
				const D3D12_STATIC_SAMPLER_DESC s_desc = {
					D3D12_FILTER_ANISOTROPIC,
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
					D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
					0, // MipLODBias
					D3D12_MAX_MAXANISOTROPY,
					D3D12_COMPARISON_FUNC_NEVER,
					D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
					0, // MinLOD
					FLT_MAX, // MaxLOD
					0, // ShaderRegister
					0, // RegisterSpace
					D3D12_SHADER_VISIBILITY_ALL,
				};
				D3D12_STATIC_SAMPLER_DESC desc = s_desc;
				desc.ShaderRegister = shaderRegister;
				desc.ShaderVisibility = shaderVisibility;
				desc.RegisterSpace = registerSpace;
				return desc;
			};

			constexpr D3D12_STATIC_SAMPLER_DESC StaticAnisotropicClamp(
				unsigned int shaderRegister,
				D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
				unsigned int registerSpace = 0u) noexcept
			{
				const D3D12_STATIC_SAMPLER_DESC s_desc = {
					D3D12_FILTER_ANISOTROPIC,
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV
					D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW
					0, // MipLODBias
					D3D12_MAX_MAXANISOTROPY,
					D3D12_COMPARISON_FUNC_NEVER,
					D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
					0, // MinLOD
					FLT_MAX, // MaxLOD
					0, // ShaderRegister
					0, // RegisterSpace
					D3D12_SHADER_VISIBILITY_ALL,
				};
				D3D12_STATIC_SAMPLER_DESC desc = s_desc;
				desc.ShaderRegister = shaderRegister;
				desc.ShaderVisibility = shaderVisibility;
				desc.RegisterSpace = registerSpace;
				return desc;
			};
		}

		struct resource_barrier
		{
			constexpr static u32 max_barrier_count{ 32u };
			constexpr void add(ID3D12Resource* resource,
							   D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after,
							   D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
							   u32 subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
				barrier.Flags = flags;
				barrier.Transition.pResource = resource;
				barrier.Transition.StateBefore = before;
				barrier.Transition.StateAfter = after;
				barrier.Transition.Subresource = subresource;

				++_offset;
			}

			constexpr void add(ID3D12Resource* resource,
							   D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
				barrier.Flags = flags;
				barrier.UAV.pResource = resource;
				++_offset;
			}

			constexpr void add(ID3D12Resource* resource_before, ID3D12Resource* resource_after,
							   D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
				barrier.Flags = flags;
				barrier.Aliasing.pResourceBefore = resource_before;
				barrier.Aliasing.pResourceAfter = resource_after;

				++_offset;
			}

			void apply(ID3D12GraphicsCommandList6* list)
			{
				list->ResourceBarrier(_offset, _barriers);
				_offset = 0u;
			}
		private:
			D3D12_RESOURCE_BARRIER _barriers[max_barrier_count]{};
			u32 _offset{};
		};

		template<u32 barrier_count>
		struct resource_barrier1
		{
			constexpr void add(ID3D12Resource* resource,
				D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after,
				D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
				u32 subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
				barrier.Flags = flags;
				barrier.Transition.pResource = resource;
				barrier.Transition.StateBefore = before;
				barrier.Transition.StateAfter = after;
				barrier.Transition.Subresource = subresource;

				++_offset;
			}

			constexpr void add(ID3D12Resource* resource,
				D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
				barrier.Flags = flags;
				barrier.UAV.pResource = resource;
				++_offset;
			}

			constexpr void add(ID3D12Resource* resource_before, ID3D12Resource* resource_after,
				D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
			{
				auto& barrier{ _barriers[_offset] };
				barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
				barrier.Flags = flags;
				barrier.Aliasing.pResourceBefore = resource_before;
				barrier.Aliasing.pResourceAfter = resource_after;

				++_offset;
			}

			void apply(ID3D12GraphicsCommandList6* list)
			{
				list->ResourceBarrier(_offset, _barriers);
				_offset = 0u;
			}
		private:
			D3D12_RESOURCE_BARRIER _barriers[barrier_count]{};
			u32 _offset{};
		};

		struct root_parameter : public D3D12_ROOT_PARAMETER
		{
			root_parameter() = default;

			constexpr void as_descriptor_table(
				u32 numDescriptorRanges,
				D3D12_DESCRIPTOR_RANGE* pDescriptorRanges,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
				this->ShaderVisibility = visibility;
				this->DescriptorTable.NumDescriptorRanges = numDescriptorRanges;
				this->DescriptorTable.pDescriptorRanges = pDescriptorRanges;
			}

			/*void as_descriptor_table(
				utl::vector<D3D12_DESCRIPTOR_RANGE>& descriptor_range_array,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
				this->ShaderVisibility = visibility;
				this->DescriptorTable.NumDescriptorRanges = static_cast<u32>(descriptor_range_array.size());
				this->DescriptorTable.pDescriptorRanges = descriptor_range_array.data();
			}*/

			constexpr void as_constants(
				u32 num32BitValues,
				u32 shaderRegister,
				u32 registerSpace = 0u,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
				this->ShaderVisibility = visibility;
				this->Constants.Num32BitValues = num32BitValues;
				this->Constants.ShaderRegister = shaderRegister;
				this->Constants.RegisterSpace = registerSpace;
			}

			constexpr void as_constant_buffer_view(
				u32 shaderRegister,
				u32 registerSpace = 0u,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
				this->ShaderVisibility = visibility;
				this->Descriptor.ShaderRegister = shaderRegister;
				this->Descriptor.RegisterSpace = registerSpace;
			}

			constexpr void as_shader_resource_view(
				u32 shaderRegister,
				u32 registerSpace = 0u,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
				this->ShaderVisibility = visibility;
				this->Descriptor.ShaderRegister = shaderRegister;
				this->Descriptor.RegisterSpace = registerSpace;
			}

			constexpr void as_unordered_access_view(
				u32 shaderRegister,
				u32 registerSpace = 0u,
				D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
			{
				this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
				this->ShaderVisibility = visibility;
				this->Descriptor.ShaderRegister = shaderRegister;
				this->Descriptor.RegisterSpace = registerSpace;
			}
		};

		struct resource_desc : D3D12_RESOURCE_DESC
		{
			resource_desc* as_buffer(u64 size)
			{
				Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
				Alignment = 0;
				Width = size;
				Height = 1;
				DepthOrArraySize = 1;
				MipLevels = 1;
				Format = DXGI_FORMAT_UNKNOWN;
				SampleDesc.Count = 1u;
				SampleDesc.Quality = 0u;
				Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
				Flags = D3D12_RESOURCE_FLAG_NONE;
				return this;
			}

			resource_desc* as_texture_buffer(u64 width, u32 height)
			{
				Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
				Alignment = 0;
				auto uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
				Width = height * uploadPitch;
				Height = 1;
				DepthOrArraySize = 1;
				MipLevels = 1;
				Format = DXGI_FORMAT_UNKNOWN;
				SampleDesc.Count = 1u;
				SampleDesc.Quality = 0u;
				Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
				Flags = D3D12_RESOURCE_FLAG_NONE;
				return this;
			}

			void as_texture_1d(u64 size)
			{
				Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
				Alignment = 0;
				Width = size;
				Height = 1;
				DepthOrArraySize = 1;
				MipLevels = 1;
				Format = DXGI_FORMAT_UNKNOWN;
				SampleDesc.Count = 1u;
				SampleDesc.Quality = 0u;
				Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
				Flags = D3D12_RESOURCE_FLAG_NONE;
			}

			resource_desc* as_texture_2d(u64 width, u32 height, DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE)
			{
				Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
				Alignment = 0;
				Width = width;
				Height = height;
				DepthOrArraySize = 1;
				MipLevels = 1;
				Format = format;
				SampleDesc.Count = 1u;
				SampleDesc.Quality = 0u;
				Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
				Flags = flags;
				return this;
			}

			void as_texture_3d(u64 width, u32 height, u16 depth)
			{
				Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
				Alignment = 0;
				Width = width;
				Height = height;
				DepthOrArraySize = depth;
				MipLevels = 1;
				Format = DXGI_FORMAT_UNKNOWN;
				SampleDesc.Count = 1u;
				SampleDesc.Quality = 0u;
				Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
				Flags = D3D12_RESOURCE_FLAG_NONE;
			}
		};

		struct texture_copy_location :D3D12_TEXTURE_COPY_LOCATION
		{
			texture_copy_location* as_buffer(ID3D12Resource* res, u32 width, u32 height, DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM, u32 depth = 1u)
			{
				Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
				PlacedFootprint.Footprint.Width = width;
				PlacedFootprint.Footprint.Height= height;
				PlacedFootprint.Footprint.Format = format;
				PlacedFootprint.Footprint.Depth = depth;
				PlacedFootprint.Footprint.RowPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
				PlacedFootprint.Offset = 0ull;
				pResource = res;
				return this;
			}

			texture_copy_location* as_texture(ID3D12Resource* res, u32 index)
			{
				Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
				SubresourceIndex = index;
				pResource = res;
				return this;
			}
		};

		struct view_desc
		{
			union
			{
				D3D12_CONSTANT_BUFFER_VIEW_DESC _cbv;
				D3D12_SHADER_RESOURCE_VIEW_DESC _srv;
				D3D12_UNORDERED_ACCESS_VIEW_DESC _uav;
				D3D12_RENDER_TARGET_VIEW_DESC _rtv;
				D3D12_DEPTH_STENCIL_VIEW_DESC _dsv;
			};

			constexpr D3D12_RENDER_TARGET_VIEW_DESC* as_rtv(DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM)
			{
				_rtv.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
				_rtv.Format = format;
				_rtv.Texture2D.MipSlice;
				return &_rtv;
			}

			constexpr D3D12_DEPTH_STENCIL_VIEW_DESC* as_dsv_texture_2d
			(
				DXGI_FORMAT format = DXGI_FORMAT_D32_FLOAT,
				D3D12_DSV_FLAGS flags = D3D12_DSV_FLAG_NONE
			)
			{
				_dsv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
				_dsv.Format = format;
				_dsv.Flags = flags;
				return &_dsv;
			}

			constexpr D3D12_SHADER_RESOURCE_VIEW_DESC* as_srv_texture_2d
			(
				DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM,
				UINT shader4componentmapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
				UINT miplevels = 1u
			)
			{
				_srv.Format = format;
				_srv.Shader4ComponentMapping = shader4componentmapping;
				_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
				_srv.Texture2D.MipLevels = miplevels;
				return &_srv;
			}

			constexpr D3D12_CONSTANT_BUFFER_VIEW_DESC* as_constant
			(
				D3D12_GPU_VIRTUAL_ADDRESS address,
				u32 size
			)
			{
				_cbv.BufferLocation = address;
				_cbv.SizeInBytes = size;
				return &_cbv;
			}
		};

		template <typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
		class alignas(void*) pipeline_state_stream_subobject
		{
		private:
			D3D12_PIPELINE_STATE_SUBOBJECT_TYPE pssType;
			InnerStructType pssInner;
		public:
			pipeline_state_stream_subobject() noexcept : pssType(Type), pssInner(DefaultArg()) {}
			pipeline_state_stream_subobject(InnerStructType const& i) noexcept : pssType(Type), pssInner(i) {}
			void operator=(InnerStructType const& i) noexcept { pssType = Type; pssInner = i; }
			void as_self() { pssType = Type; }
			operator InnerStructType const& () const noexcept { return pssInner; }
			operator InnerStructType& () noexcept { return pssInner; }
			InnerStructType* operator&() noexcept { return &pssInner; }
			InnerStructType const* operator&() const noexcept { return &pssInner; }
		};

		using pipeline_state_stream_flags = pipeline_state_stream_subobject< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS >;
		using pipeline_state_stream_node_mask = pipeline_state_stream_subobject< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK >;
		using pipeline_state_stream_root_signature = pipeline_state_stream_subobject< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE >;
		using pipeline_state_stream_input_layout = pipeline_state_stream_subobject< D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT >;
		using pipeline_state_stream_ib_strip_cut_value = pipeline_state_stream_subobject< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE >;
		using pipeline_state_stream_primitive_topology = pipeline_state_stream_subobject< D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY >;
		using pipeline_state_stream_vs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS >;
		using pipeline_state_stream_gs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS >;
		using pipeline_state_stream_stream_output = pipeline_state_stream_subobject< D3D12_STREAM_OUTPUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT >;
		using pipeline_state_stream_hs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS >;
		using pipeline_state_stream_ds = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS >;
		using pipeline_state_stream_ps = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS >;
		using pipeline_state_stream_as = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS >;
		using pipeline_state_stream_ms = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS >;
		using pipeline_state_stream_cs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS >;
		using pipeline_state_stream_blend_desc = pipeline_state_stream_subobject< D3D12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND >;
		using pipeline_state_stream_depth_stencil = pipeline_state_stream_subobject< D3D12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL >;
		using pipeline_state_stream_depth_stencil1 = pipeline_state_stream_subobject< D3D12_DEPTH_STENCIL_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1 >;
		using pipeline_state_stream_depth_stencil_format = pipeline_state_stream_subobject< DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT >;
		using pipeline_state_stream_rasterizer = pipeline_state_stream_subobject< D3D12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER >;
		using pipeline_state_stream_render_target_formats = pipeline_state_stream_subobject< D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS >;
		using pipeline_state_stream_sample_desc = pipeline_state_stream_subobject< DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC >;
		using pipeline_state_stream_sample_mask = pipeline_state_stream_subobject< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK >;
		using pipeline_state_stream_cached_pso = pipeline_state_stream_subobject< D3D12_CACHED_PIPELINE_STATE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO >;
		using pipeline_state_stream_view_instancing = pipeline_state_stream_subobject< D3D12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING >;


		constexpr u64 stream_array[] =
		{
			sizeof(pipeline_state_stream_flags),
			sizeof(pipeline_state_stream_node_mask),
			sizeof(pipeline_state_stream_root_signature),
			sizeof(pipeline_state_stream_input_layout),
			sizeof(pipeline_state_stream_ib_strip_cut_value),
			sizeof(pipeline_state_stream_primitive_topology),
			sizeof(pipeline_state_stream_vs),
			sizeof(pipeline_state_stream_gs),
			sizeof(pipeline_state_stream_stream_output),
			sizeof(pipeline_state_stream_hs),
			sizeof(pipeline_state_stream_ds),
			sizeof(pipeline_state_stream_ps),
			sizeof(pipeline_state_stream_as),
			sizeof(pipeline_state_stream_ms),
			sizeof(pipeline_state_stream_cs),
			sizeof(pipeline_state_stream_blend_desc),
			sizeof(pipeline_state_stream_depth_stencil),
			sizeof(pipeline_state_stream_depth_stencil1),
			sizeof(pipeline_state_stream_depth_stencil_format),
			sizeof(pipeline_state_stream_rasterizer),
			sizeof(pipeline_state_stream_render_target_formats),
			sizeof(pipeline_state_stream_sample_desc),
			sizeof(pipeline_state_stream_sample_mask),
			sizeof(pipeline_state_stream_cached_pso),
			sizeof(pipeline_state_stream_view_instancing),
		};


		constexpr u64 pipeline_state_desc_size
		{
			[]()->u64
			{
			   u64 sub{};
			   for (size_t i = 0; i < _countof(stream_array); i++)
			   {
				   sub += stream_array[i];
			   }
			   return sub;
			}()
		};

		struct pipeline_state_stream_desc : D3D12_PIPELINE_STATE_STREAM_DESC
		{
			pipeline_state_stream_desc()
			{
				this->pPipelineStateSubobjectStream = malloc(pipeline_state_desc_size);
				this->SizeInBytes = 0;
			}
			~pipeline_state_stream_desc()
			{
				free(this->pPipelineStateSubobjectStream);
			}

			template<typename T>
			void add(const T& sub)
			{
				T* current_data = reinterpret_cast<T*>(reinterpret_cast<u64>(this->pPipelineStateSubobjectStream) + this->SizeInBytes);
				*current_data = sub;
				this->SizeInBytes += sizeof(T);
			}

			template<typename T>
			T& add()
			{
				T* current_data = reinterpret_cast<T*>(reinterpret_cast<u64>(this->pPipelineStateSubobjectStream) + this->SizeInBytes);
				this->SizeInBytes += sizeof(T);
				current_data->as_self();
				return *current_data;
			}
		};
	}
}